
# 从 qtpy 库导入 QtWidgets 模块，用于创建和管理 GUI 界面组件
from qtpy import QtWidgets
# 从 qtpy 库导入 QtGui 和 QtCore 模块，用于处理图形和核心功能
from qtpy import QtGui, QtCore

# 从 src 框架的 UI.Canvas.Painters 模块导入 PinPainter 类，用于绘制引脚
from src.UI.Canvas.Painters import PinPainter
# 从 src 框架导入查找引脚类和获取所有引脚类的函数
from src import findPinClassByType, getAllPinClasses
# 从 src 框架的 Core.Common 模块导入 PinDirection 枚举，用于表示引脚方向
from src.Core.Common import PinDirection

# 定义引脚的大小常量
_PIN_SIZE = 15


class _FakeCanvas(object):
    """
    模拟画布类，用于提供画布相关的功能模拟。
    主要模拟获取细节级别（LOD）值的方法。
    """
    def __init__(self):
        # 调用父类的构造函数
        super(_FakeCanvas, self).__init__()

    def getLodValueFromCurrentScale(self, lod):
        """
        根据当前缩放比例获取细节级别值。
        这里固定返回 1。

        :param lod: 细节级别参数
        :return: 细节级别值，固定为 1
        """
        return 1

    def getCanvasLodValueFromCurrentScale(self):
        """
        获取画布的细节级别值。
        这里固定返回 1。

        :return: 画布的细节级别值，固定为 1
        """
        return 1


class _FakeNode(object):
    """
    模拟节点类，用于提供节点相关的功能模拟。
    主要包含一个模拟画布的引用。
    """
    def __init__(self):
        # 调用父类的构造函数
        super(_FakeNode, self).__init__()
        # 创建一个模拟画布的实例
        self.fakeCanvas = _FakeCanvas()

    def canvasRef(self):
        """
        获取节点引用的画布。

        :return: 模拟画布的实例
        """
        return self.fakeCanvas


class _FakePin(object):
    """
    模拟引脚类，用于提供引脚相关的功能模拟。
    主要模拟引脚的连接状态。
    """
    def __init__(self):
        # 调用父类的构造函数
        super(_FakePin, self).__init__()
        # 初始化引脚的连接状态为未连接
        self.connected = False

    def setConnected(self, connected):
        """
        设置引脚的连接状态。

        :param connected: 布尔值，表示引脚是否连接
        """
        self.connected = connected

    def hasConnections(self):
        """
        判断引脚是否有连接。

        :return: 布尔值，表示引脚是否连接
        """
        return self.connected


class _PinWidget(QtWidgets.QWidget):
    """
    引脚小部件类，用于在界面上显示和操作引脚。

    :param dataType: 引脚的数据类型
    :param parent: 父级小部件，默认为 None
    """

    def __init__(self, dataType, parent=None):
        # 调用父类的构造函数
        super(_PinWidget, self).__init__(parent)
        # 存储引脚的数据类型
        self.dataType = dataType
        # 创建一个模拟拥有该引脚的节点实例
        self.fakeOwningNode = _FakeNode()
        # 创建一个模拟引脚的实例
        self._rawPin = _FakePin()
        # 根据引脚的数据类型查找对应的引脚类，并获取其颜色
        self._pinColor = QtGui.QColor(*findPinClassByType(self.dataType).color())
        # 此处存在类 'Qt' 的未解析的特性引用 'white' 问题，设置标签颜色
        self.labelColor = QtCore.Qt.white
        # 标记引脚是否被悬停
        self.hovered = False
        # 设置引脚的大小
        self.pinSize = _PIN_SIZE
        # 创建一个字体对象，使用 Consolas 字体
        self._font = QtGui.QFont("Consolas")
        # 设置字体的大小为 14
        self._font.setPointSize(14)
        # 设置引脚的方向为输入方向
        self.direction = PinDirection.Input
        # 设置引脚的名称为其数据类型
        self.name = self.dataType
        # 标记引脚标签是否隐藏
        self.bLabelHidden = False
        # 设置引脚圆形绘制的偏移量
        self.pinCircleDrawOffset = QtCore.QPointF()

        # 启用鼠标跟踪功能，以便捕获鼠标悬停事件
        self.setMouseTracking(True)
        # 此处存在类 'QSizePolicy' 的未解析的特性引用 'Maximum' 问题，设置小部件的大小策略
        self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)

    def sizeHint(self):
        """
        获取小部件的建议大小。
        根据引脚数据类型的文本宽度和高度计算。

        :return: 小部件的建议大小，QtCore.QSize 对象
        """
        # 计算文本的宽度，包括引脚大小
        textWidth = (
            QtGui.QFontMetrics(self._font).horizontalAdvance(self.dataType) + _PIN_SIZE
        )
        # 计算文本的高度，取字体高度和引脚大小加 6 中的最大值
        textHeight = max(QtGui.QFontMetrics(self._font).height(), _PIN_SIZE + 6)
        return QtCore.QSize(textWidth, textHeight)

    def pinCenter(self):
        """
        获取引脚的中心点位置。

        :return: 引脚的中心点位置，QtCore.QPointF 对象
        """
        return QtCore.QPointF(_PIN_SIZE, _PIN_SIZE / 2)

    def displayName(self):
        """
        获取引脚的显示名称。

        :return: 引脚的显示名称，即数据类型
        """
        return self.dataType

    def owningNode(self):
        """
        获取拥有该引脚的节点。

        :return: 模拟拥有该引脚的节点实例
        """
        return self.fakeOwningNode

    def enterEvent(self, event):
        """
        处理鼠标进入小部件的事件。
        标记引脚为悬停状态，并更新小部件的显示。

        :param event: 鼠标进入事件对象
        """
        super(_PinWidget, self).enterEvent(event)
        self.hovered = True
        self.update()

    def leaveEvent(self, event):
        """
        处理鼠标离开小部件的事件。
        标记引脚为非悬停状态，并更新小部件的显示。

        :param event: 鼠标离开事件对象
        """
        super(_PinWidget, self).leaveEvent(event)
        self.hovered = False
        self.update()

    def color(self):
        """
        获取引脚的颜色。

        :return: 引脚的颜色，QtGui.QColor 对象
        """
        return self._pinColor

    @property
    def width(self):
        """
        获取小部件的宽度。
        为最小宽度减去 5。

        :return: 小部件的宽度
        """
        return self.minimumWidth() - 5

    @property
    def height(self):
        """
        获取小部件的高度。
        为最小高度减去 5。

        :return: 小部件的高度
        """
        return self.minimumHeight() - 5

    def paintEvent(self, event):
        """
        处理小部件的绘制事件。
        根据引脚的数据类型，使用不同的绘制方法绘制引脚。

        :param event: 绘制事件对象
        """
        # 创建一个 QPainter 对象，用于绘制图形
        painter = QtGui.QPainter()
        # 开始在小部件上绘制
        painter.begin(self)
        # 此处存在类 'Qt' 的未解析的特性引用 'NoPen' 问题，设置画笔为无画笔
        painter.setPen(QtCore.Qt.NoPen)
        if self.dataType == "ExecPin":
            # 如果引脚的数据类型为 ExecPin，将模拟引脚设置为连接状态，并使用 ExecPin 绘制方法
            self._rawPin.setConnected(True)
            PinPainter.asExecPin(self, painter, None, None)
        else:
            # 否则，使用值引脚的绘制方法
            PinPainter.asValuePin(self, painter, None, None)
        # 结束绘制
        painter.end()


class _PinsListWidget(QtWidgets.QListWidget):
    """
    引脚列表小部件类，用于显示和管理引脚列表。
    提供了过滤列表内容、创建列表项等功能。

    :param parent: 父级小部件，默认为 None
    :param validPins: 有效的引脚列表，默认为 None
    """

    # 定义一个信号，当按下回车键时发射
    returnPressed = QtCore.Signal()

    def __init__(self, parent=None, validPins=None):
        # 调用父类的构造函数
        super(_PinsListWidget, self).__init__()
        # 填充引脚列表，使用指定的过滤模式和有效引脚列表
        self.populate(pattern="", validPins=validPins)

    def filterContent(self, pattern):
        """
        根据指定的模式过滤引脚列表的内容。
        先清空列表，再根据模式重新填充。

        :param pattern: 过滤模式，字符串类型
        """
        self.clear()
        self.populate(pattern)

    def createEntry(self, dataType):
        """
        创建一个引脚列表项。
        根据指定的数据类型创建引脚小部件，并添加到列表中。

        :param dataType: 引脚的数据类型
        """
        # 创建一个引脚小部件实例
        widget = _PinWidget(dataType)
        # 创建一个列表项实例
        item = QtWidgets.QListWidgetItem(self)
        # 将引脚小部件设置为列表项的小部件
        self.setItemWidget(item, widget)

    def keyPressEvent(self, event):
        """
        处理键盘按键事件。
        当按下回车键时，发射 returnPressed 信号。

        :param event: 键盘按键事件对象
        """
        # 此处存在类 'Qt' 的未解析的特性引用 'Key_Return' 问题，检查是否按下回车键
        if event.key() == QtCore.Qt.Key_Return:
            self.returnPressed.emit()
        # 调用父类的键盘按键事件处理方法
        super(_PinsListWidget, self).keyPressEvent(event)

    def populate(
        self, pattern="", validPins=None
    ):
        """
        填充引脚列表。
        根据指定的过滤模式和有效引脚列表，创建并添加引脚列表项。

        :param pattern: 过滤模式，字符串类型，默认为空字符串
        :param validPins: 有效的引脚列表，默认为 None
        """
        if validPins is None:
            # 如果未指定有效引脚列表，则获取所有引脚类的名称作为有效引脚列表
            validPins = [pin.__name__ for pin in getAllPinClasses()]
        # 遍历所有引脚类
        for pinClass in getAllPinClasses():
            # 获取引脚类的名称
            className = pinClass.__name__
            if className in validPins:
                if len(pattern) > 0:
                    # 如果指定了过滤模式，且模式在类名中（忽略大小写），则创建列表项
                    if pattern.lower() in className.lower():
                        self.createEntry(className)
                else:
                    # 否则，直接创建列表项
                    self.createEntry(className)
        # 设置当前选中的行索引为 0
        self.setCurrentRow(0)


class SelectPinDialog(QtWidgets.QDialog):
    """
    选择引脚对话框类，用于让用户选择一个引脚。
    提供了搜索过滤、选择引脚等功能。

    :param parent: 父级小部件，默认为 None
    :param validPins: 有效的引脚列表，默认为 None
    """

    def __init__(self, parent=None, validPins=None):
        # 调用父类的构造函数
        super(SelectPinDialog, self).__init__(None)
        # 此处存在类 'Qt' 的未解析的特性引用 'Window' 和 'FramelessWindowHint' 问题，设置窗口标志
        self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint)
        # 设置窗口的标题
        self.setWindowTitle("Select pin")
        # 设置窗口的图标
        self.setWindowIcon(QtGui.QIcon(":/pin.png"))
        # 设置窗口的大小
        self.resize(QtCore.QSize(400, 300))

        # 创建一个垂直布局管理器，并设置为对话框的布局
        self.mainLayout = QtWidgets.QVBoxLayout(self)
        # 设置布局的边距
        self.mainLayout.setContentsMargins(2, 2, 2, 2)
        # 创建一个行编辑框，用于输入搜索模式
        self.searchBox = QtWidgets.QLineEdit()
        # 设置行编辑框的占位符文本
        self.searchBox.setPlaceholderText("search...")
        # 连接行编辑框的文本变化信号到 filterContent 方法
        self.searchBox.textChanged.connect(self.filterContent)
        # 将行编辑框添加到布局中
        self.mainLayout.addWidget(self.searchBox)
        if not validPins:
            # 如果未指定有效引脚列表，则获取所有引脚类的名称作为有效引脚列表
            validPins = [pin.__name__ for pin in getAllPinClasses()]
        # 创建一个引脚列表小部件实例
        self.content = _PinsListWidget(validPins=validPins)
        # 将引脚列表小部件添加到布局中
        self.mainLayout.addWidget(self.content)
        # 连接引脚列表小部件的项点击信号到 onItemClicked 方法
        self.content.itemClicked.connect(self.onItemClicked)
        # 连接引脚列表小部件的 returnPressed 信号到 onReturnPressed 方法
        self.content.returnPressed.connect(self.onReturnPressed)

        # 存储用户选择的结果
        self._result = None

    def onReturnPressed(self):
        """
        处理回车键按下事件。
        获取当前选中的列表项的引脚小部件，并将其数据类型作为选择结果，然后关闭对话框。
        """
        # 获取当前选中的列表项的引脚小部件
        widget = self.content.itemWidget(self.content.currentItem())
        # 此处存在类 'QWidget' 的未解析的特性引用 'dataType' 问题，将引脚小部件的数据类型作为选择结果
        self._result = widget.dataType
        # 关闭对话框
        self.close()

    def onItemClicked(self, item):
        """
        处理列表项点击事件。
        获取点击的列表项的引脚小部件，并将其数据类型作为选择结果，然后关闭对话框。

        :param item: 被点击的列表项
        """
        # 获取点击的列表项的引脚小部件
        widget = self.content.itemWidget(item)
        # 此处存在类 'QWidget' 的未解析的特性引用 'dataType' 问题，将引脚小部件的数据类型作为选择结果
        self._result = widget.dataType
        # 关闭对话框
        self.close()

    def showEvent(self, event):
        """
        处理对话框显示事件。
        在对话框显示时，将其移动到鼠标光标位置。

        :param event: 显示事件对象
        """
        # 调用父类的显示事件处理方法
        super(SelectPinDialog, self).showEvent(event)
        # 将对话框移动到鼠标光标位置
        self.move(QtGui.QCursor.pos())

    def filterContent(self, pattern):
        """
        根据指定的模式过滤引脚列表的内容。

        :param pattern: 过滤模式，字符串类型
        """
        self.content.filterContent(pattern)

    def getResult(self):
        """
        获取用户选择的结果。

        :return: 用户选择的引脚数据类型
        """
        return self._result